package vg.modules.opener.system;


import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.almworks.sqlite4java.SQLiteConnection;
import com.almworks.sqlite4java.SQLiteJob;
import com.almworks.sqlite4java.SQLiteQueue;
import com.almworks.sqlite4java.SQLiteStatement;

import vg.services.data_base_manager.data.header.AttributeHeader;
import vg.services.data_base_manager.data.header.GraphModelHeader;
import vg.services.data_base_manager.data.header.VertexHeader;
import vg.services.data_base_manager.interfaces.IModel;
import vg.services.data_base_manager.realization.DataBaseFields;
import vg.services.data_base_manager.realization.DataBaseTypeParser;
import vg.services.main_manager.MainManager;
import vg.services.progress_manager.interfaces.IProgressTask;

public class VGDBDecoder implements IDecoder {
	private long current_progress = 0;
	private long general_progress = 0;
	private boolean finish_job = false;
	private SQLiteQueue currConnection;
	@Override
	public List<Integer> decode(String fileName, final IModel model, String graphName) throws Exception {
		currConnection = new SQLiteQueue(new File(fileName));
		currConnection.start();
	
		calculate_progress_bar();
		
		final Map<Integer, Integer> old_new_graph_model_com = new HashMap<Integer, Integer>();
		final Map<Integer, Integer> old_new_graph_com = new HashMap<Integer, Integer>();
		final Map<Integer, Integer> old_new_vertex_com = new HashMap<Integer, Integer>();
		final Map<Integer, Integer> old_new_edge_com = new HashMap<Integer, Integer>();
		
		List<Integer> new_graph_id = new ArrayList<Integer>();
		Map<Integer, GraphModelHeader> graph_with_old_root_key = new HashMap<Integer, GraphModelHeader>();
		
		List<List<Object>> result_sql_request = null;

		// select graph model data
		String get_graph_model_request = "select * " +
                               "from " + DataBaseFields.GRAPH_MODEL_TABLE + ";";
		result_sql_request = executeSQLRequest(get_graph_model_request);
		for(List<Object> buf : result_sql_request) {
			Integer old_id = (Integer)buf.get(0);
			Integer old_root_id = (Integer)buf.get(1);
			String old_name = (String)buf.get(2);
			int new_id = model.createGraphModelHeader(old_name);
			old_new_graph_model_com.put(old_id, new_id);
			GraphModelHeader curr_header = new GraphModelHeader(new_id);
			curr_header.name = old_name;
			curr_header.root_id = old_root_id;
			graph_with_old_root_key.put(new_id, curr_header);
			new_graph_id.add(new_id);
		}
		current_progress += result_sql_request.size();
		
		// select graph data 
		SQLiteJob<List<List<Object>>> job = this.currConnection.execute(new SQLiteJob<List<List<Object>>>() {
			protected List<List<Object>> job(SQLiteConnection connection) throws Throwable {
				String get_graph_request = "select * " +
		        "from " + DataBaseFields.GRAPH_TABLE + ";";
				SQLiteStatement currStatement = connection.prepare(get_graph_request, false);
				while (currStatement.step()) {
					Integer old_id = (Integer)currStatement.columnValue(0);
					Integer old_g_id = (Integer)currStatement.columnValue(1);
					Boolean old_directed = Boolean.valueOf(currStatement.columnValue(2).toString());
					if(old_new_graph_model_com.containsKey(old_g_id)) {
						int new_id = model.createGraphHeader(old_new_graph_model_com.get(old_g_id), old_directed);
						old_new_graph_com.put(old_id, new_id);
					}
					current_progress++;
				}
				currStatement.dispose();
				return null;
			}
		});
		job.complete();
		if (job.getError() != null) {
			finish_job = true;
			MainManager.log.printException(job.getError());
			return null;
		}
		
		// select vertex data		
		job = this.currConnection.execute(new SQLiteJob<List<List<Object>>>() {
			protected List<List<Object>> job(SQLiteConnection connection) throws Throwable {
				String get_vertex_request = "select * " +
		        "from " + DataBaseFields.VERTEX_TABLE + ";";
				SQLiteStatement currStatement = connection.prepare(get_vertex_request, false);
				while (currStatement.step()) {
					Integer old_id = (Integer)currStatement.columnValue(0);
					Integer old_sg_id = (Integer)currStatement.columnValue(1);
					Integer old_link2inner_graph = (Integer)currStatement.columnValue(2);
					if(old_new_graph_com.containsKey(old_sg_id)) {
						int new_link2inner_graph = VertexHeader.DEF_NO_LINK2INNER_GRAPH;
						if(old_new_graph_com.containsKey(old_link2inner_graph)) {
							new_link2inner_graph = old_new_graph_com.get(old_link2inner_graph);
						}
						int new_id = model.createVertexHeader(old_new_graph_com.get(old_sg_id), new_link2inner_graph);
						old_new_vertex_com.put(old_id, new_id);
					}
					current_progress++;
				}
				currStatement.dispose();
				return null;
			}
		});
		job.complete();
		if (job.getError() != null) {
			finish_job = true;
			MainManager.log.printException(job.getError());
			return null;
		}
		
		// select edge data
		job = this.currConnection.execute(new SQLiteJob<List<List<Object>>>() {
			protected List<List<Object>> job(SQLiteConnection connection) throws Throwable {
				String get_edge_request = "select * " +
		        "from " + DataBaseFields.EDGE_TABLE + ";";
				SQLiteStatement currStatement = connection.prepare(get_edge_request, false);
				while (currStatement.step()) {
					Integer old_id = (Integer)currStatement.columnValue(0);
					Integer old_sg_id = (Integer)currStatement.columnValue(1);
					Integer old_src = (Integer)currStatement.columnValue(2);
					Integer old_trg = (Integer)currStatement.columnValue(3);
					if(old_new_graph_com.containsKey(old_sg_id)) {
						if(old_new_vertex_com.containsKey(old_src) && old_new_vertex_com.containsKey(old_trg)) {
							int new_id = model.createEdgeHeader(old_new_graph_com.get(old_sg_id), old_new_vertex_com.get(old_src), old_new_vertex_com.get(old_trg));
							old_new_edge_com.put(old_id,new_id);
						}
					}
					current_progress++;
				}
				currStatement.dispose();
				return null;
			}
		});
		job.complete();
		if (job.getError() != null) {
			finish_job = true;
			MainManager.log.printException(job.getError());
			return null;
		}

		// select attribute data
		job = this.currConnection.execute(new SQLiteJob<List<List<Object>>>() {
			@Override
			protected List<List<Object>> job(SQLiteConnection connection) throws Throwable {
				String get_attribute_request = "select * " +
		        "from " + DataBaseFields.ATTRIBUTE_TABLE + ";";
				SQLiteStatement currStatement = connection.prepare(get_attribute_request, false);
				while (currStatement.step()) {
					//Integer old_id = (Integer)buf.get(0);
					Integer old_owner_id = (Integer)currStatement.columnValue(1);
					Integer old_owner_type = (Integer)currStatement.columnValue(2);
					String old_name = (String)currStatement.columnValue(3);
					String old_value = (String)currStatement.columnValue(4);
					String old_type = (String)currStatement.columnValue(5);
					switch(old_owner_type) {
						case AttributeHeader.DEF_VERTEX_OWNER_TYPE: {
							if(old_new_vertex_com.containsKey(old_owner_id)) {
								model.createAttributeHeader4Vertex(old_new_vertex_com.get(old_owner_id), old_name, old_value, DataBaseTypeParser.string2class(old_type));
							}
							break;
						}
						case AttributeHeader.DEF_EDGE_OWNER_TYPE: {
							if(old_new_edge_com.containsKey(old_owner_id)) {
								model.createAttributeHeader4Edge(old_new_edge_com.get(old_owner_id), old_name, old_value, DataBaseTypeParser.string2class(old_type));
							}					
							break;
						}
					}
					current_progress++;
				}
				currStatement.dispose();
				return null;
			}
		});
		job.complete();
		if (job.getError() != null) {
			finish_job = true;
			MainManager.log.printException(job.getError());
			return null;
		}

		// add root key
		for(GraphModelHeader buf : graph_with_old_root_key.values()) {
			if(old_new_graph_com.containsKey(buf.root_id)) {
				buf.root_id = old_new_graph_com.get(buf.root_id);
				model.modifyGraphModelHeader(buf);
			}
		}
		
		finish_job = true;
		return new_graph_id;
	}
	
	/**
	 * This method executes SQL request. 
	 * @param request - SQL request.
	 */
	private List<List<Object>> executeSQLRequest(final String request) {
		SQLiteJob<List<List<Object>>> job = this.currConnection.execute(new SQLiteJob<List<List<Object>>>() {
			protected List<List<Object>> job(SQLiteConnection connection) throws Throwable {
				SQLiteStatement currStatement = connection.prepare(request, false);
				List<List<Object>> data = new ArrayList<List<Object>>();
				while (currStatement.step()) {
					ArrayList<Object> row = new ArrayList<Object>();
					for (int i = 0; i < currStatement.columnCount(); i++) {
						row.add(currStatement.columnValue(i));
					}
					data.add(row);
				}
				currStatement.dispose();
				return data;
			}
		});
		List<List<Object>> data = job.complete();
		Throwable ex = job.getError();
		if (ex != null) {
			MainManager.log.printException(ex);
			return null;
		}
		return data;
	}
	
	private void calculate_progress_bar() {
		// calculate general rows in data base
		List<List<Object>> result_sql_request = null;

		String get_count_graph_model_request = "select count(*) " + "from " + DataBaseFields.GRAPH_MODEL_TABLE + ";";
		result_sql_request = executeSQLRequest(get_count_graph_model_request);
		if(result_sql_request != null && result_sql_request.size() > 0) {
			general_progress += (Integer)result_sql_request.get(0).get(0);
		}
		
		String get_count_graph_request = "select count(*) " + "from " + DataBaseFields.GRAPH_TABLE + ";";
		result_sql_request = executeSQLRequest(get_count_graph_request);
		if(result_sql_request != null && result_sql_request.size() > 0) {
			general_progress += (Integer)result_sql_request.get(0).get(0);
		}

		String get_count_vertex_request = "select count(*) " + "from " + DataBaseFields.VERTEX_TABLE + ";";
		result_sql_request = executeSQLRequest(get_count_vertex_request);
		if(result_sql_request != null && result_sql_request.size() > 0) {
			general_progress += (Integer)result_sql_request.get(0).get(0);
		}

		String get_count_edge_request = "select count(*) " + "from " + DataBaseFields.EDGE_TABLE + ";";
		result_sql_request = executeSQLRequest(get_count_edge_request);
		if(result_sql_request != null && result_sql_request.size() > 0) {
			general_progress += (Integer)result_sql_request.get(0).get(0);
		}

		String get_count_attribute_request = "select count(*) " + "from " + DataBaseFields.ATTRIBUTE_TABLE + ";";
		result_sql_request = executeSQLRequest(get_count_attribute_request);
		if(result_sql_request != null && result_sql_request.size() > 0) {
			general_progress += (Integer)result_sql_request.get(0).get(0);
		}

		MainManager.log.printDebug("Openning of VGDB file. General rows = " + general_progress);
		
		
		IProgressTask task = new IProgressTask() {
			@Override
			public long getValue() {
				if(finish_job)
					return general_progress;
				return current_progress;
			}

			@Override
			public long getLength() {
				return general_progress;
			}

			@Override
			public String getTaskName() {
				return "Open VGDB file";
			}
		};

		MainManager.progressManager.addTask(task);
	}
}
